﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpSoft.Lexing
{
    /// <summary>
    /// 文本词法分析程序基类。
    /// 为避免出现逻辑异常，词法分析器必须遵循以下规则：
    /// 读取每个Token时从当前位置开始读取，读取完毕后一定要将游标置于Token后的第一个字符上。
    /// 对于没有明确范围的循环读取，总是应在循环中检查EOF()以避免出现死循环。如：读取字符串标记时。
    /// </summary>
    public abstract class _TextLexer
    {
        public _TextLexer(string source)
        {
            this._Source = new TextSource(source);
        }
        public _TextLexer(ITextSource source)
        {
            _Source = source;
        }
        private ITextSource _Source;
        /// <summary>
        /// 获取词法分析器当前的源
        /// </summary>
        public ITextSource Source
        {
            get { return _Source; }
        }
        ///// <summary>
        ///// 从当前位置开始读取一个Token
        ///// </summary>
        ///// <returns></returns>
        //public abstract IToken ReadToken();

        public virtual int Position
        {
            get
            {
                return Source.Position;
            }
        }

        public virtual char Current
        {
            get { return Source.Current; }
        }

        public virtual bool EOS()
        {
            return Source.EOS();
        }
        public virtual char Peek(int offset = 1)
        {
            return Source.Peek(offset);
        }

        public virtual char Next(int offset = 1)
        {
            return Source.Next(offset);
        }

        /// <summary>
        /// 跳过空白字符
        /// </summary>
        public virtual void SkipWhiteSpace()
        {
            while (Current.IsWhiteSpace())
            {
                Source.MoveCursor();
                if (EOS()) return;
            }
        }
        /// <summary>
        /// 当前字符无效，抛出异常。
        /// </summary>
        [System.Diagnostics.DebuggerStepThrough]
        public virtual void InvalidChar()
        {
            throw new TextLexerException(Position, 1, "意外的字符：[" + Current + "]");
        }

        #region ReadHelper


        /// <summary>
        /// 从当前位置开始读取一个字符串（用双引号或单引号括起来的内容）
        /// </summary>
        /// <returns></returns>
        public string ReadString()
        {
            if (Current != '\"' && Current != '\'')
                throw new TextLexerException(Position, 0, "字符串必须以双引号或单引号开始。");
            StringBuilder sb = new StringBuilder(10);
            char q = Current;
            char c = Next();
            while (c != q)//一直循环直到遇到结束引号
            {
                if (EOS())
                {//字符串尚未结束，但是遇到了文档结束标识
                    throw new TextLexerException(Position, 0, "字符串未正常闭合。");
                }
                if (c == '\\')
                {//读取转义字符
                    sb.Append(ReadEscapeCharacter());
                    c = Current;
                    continue;
                }
                sb.Append(c);
                c = Next();
            }
            Source.MoveCursor();//跳过字符串结束标记
            return sb.ToString();
        }

        /// <summary>
        /// 读取两个特殊字符之间的内容
        /// </summary>
        /// <param name="head">头字符</param>
        /// <param name="tail">尾字符</param>
        /// <returns></returns>
        public string ReadBetween(char head, char tail)
        {
            char c = Current;
            if (c != head)
                throw new TextLexerException(Position, 1, string.Format("此位置字符不是[{0}]。", head));
            StringBuilder sb = new StringBuilder(5);
            c = Next();
            while (c != tail)
            {
                if (EOS())
                    throw new TextLexerException(Position, 1, string.Format("尝试读取字符'{0}'和'{1}'之间的内容，但是直到源结束也未出现'{1}'字符。", head, tail));
                sb.Append(c);
                c = Next();
            }
            Source.MoveCursor();
            return sb.ToString();
        }

        /// <summary>
        /// 尝试跳过指定的首尾标记包围的字符。（也包括首尾标记，一般用于跳过注释）
        /// </summary>
        /// <param name="head"></param>
        /// <param name="end"></param>
        public bool SkipBetween(string head, string end)
        {
            if (TryMatch(head, false))
            {
                while (!EOS())
                {
                    if (TryMatch(end))
                    {
                        return true;//已匹配结束标记
                    }
                    Next();
                }
            }
            return false;
        }
        /// <summary>
        /// 将游标置于下一个指定的字符上（包括当前字符,如果当前字符即为指定字符则立即返回true）
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public bool SkipTo(char c)
        {
            char curc = Current;
            while (c != curc)
            {
                if (EOS())
                {
                    return false;
                }
                curc = Next();
            }
            return true;
        }
        /// <summary>
        /// 将游标置于下一个指定的字符串开头,若找不到该字符串则将游标还原到当前位置。
        /// </summary>
        /// <param name="str"></param>
        /// <returns>匹配到指定的字符串返回true,否则返回false.</returns>
        public bool SkipTo(string matchstr)
        {
            int pos = Position;
            while (!EOS())
            {
                if (TryMatch(matchstr, false))
                {
                    return true;
                }
                else
                {
                    Source.MoveCursor();
                }
            }
            Source.Set(pos);//未找到，还原游标
            return false;
        }
        /// <summary>
        /// 将游标置于下一个指定的字符类别上
        /// </summary>
        /// <param name="chartype"></param>
        /// <returns></returns>
        public bool SkipTo(CharCategory chartype)
        {
            if (chartype == CharCategory.Unknow)
            {
                throw new Exception("无法跳转到未知类型的字符。");
            }
            char curc = Current;
            while (!EOS())
            {
                if (curc.MatchCategory(chartype))
                {//当前字符在指定的类型之列
                    return true;
                }
                curc = Next();
            }

            return false;
        }



        /// <summary>
        /// 尝试在当前位置匹配一个字符串
        /// </summary>
        /// <param name="matchstr">指定要匹配的字符串</param>
        /// <param name="autoreturn">指定若不能匹配是否自动回退到匹配开始的位置（默认为true）。</param>
        /// <returns></returns>
        public bool TryMatch(string matchstr, bool autoreturn = true)
        {
            int pos = Position;//记录匹配前的位置
            bool ismatch = true;
            foreach (var item in matchstr)
            {
                if (Current != item)
                {
                    ismatch = false;
                    break;
                }
                Source.MoveCursor();
            }
            if (autoreturn && !ismatch)
            {
                Source.Set(pos);//返回匹配之前的位置
            }
            return ismatch;
        }


        /// <summary>
        /// 从当前位置开始读取一个转义字符(注意：转义字符中返回的'\0'不能作为结束标识)
        /// </summary>
        /// <returns></returns>
        public char ReadEscapeCharacter()
        {
            if (Current != '\\')
                throw new Exception("当前字符不是\\，无法读取转义字符");
            char c = Next();
            if (c == '\\')
            {
                Source.MoveCursor();
                return '\\';
            }
            else if (c == 'u')
            {//Unicode转义序列
                Source.MoveCursor();
                string hex = ReadHex(4);
                if (hex.Length != 4)//Unicode的16进制转义字符个数必须为4
                    throw new InvalidEscapeSequenceException(Position, hex);
                short value = Convert.ToInt16(hex, 16);
                return (char)value;
            }
            else if (c == 'x')
            {//ASCII转义序列，当六进制的位数大于2位时自动转为unicode转义序列
                Source.MoveCursor();
                string hex = ReadHex(4);
                if (hex == string.Empty)
                    throw new InvalidEscapeSequenceException(Position, Current.ToString());
                short value = Convert.ToInt16(hex, 16);
                return (char)value;
            }
            else if (c == 'a')//铃声
            {
                Source.MoveCursor();
                return '\a';
            }
            else if (c == 'b')//Backspace
            {
                Source.MoveCursor();
                return '\b';
            }
            else if (c == 'f')//换页
            {
                Source.MoveCursor();
                return '\f';
            }
            else if (c == 'n')//换行
            {
                Source.MoveCursor();
                return '\n';
            }
            else if (c == 'r')//回车
            {
                Source.MoveCursor();
                return '\r';
            }
            else if (c == 't')//Tab,水平制表符
            {
                Source.MoveCursor();
                return '\t';
            }
            else if (c == 'v')//垂直制表符
            {
                Source.MoveCursor();
                return '\v';
            }
            else if (c == '\'')//单引号
            {
                Source.MoveCursor();
                return '\'';
            }
            else if (c == '\"')//双引号
            {
                Source.MoveCursor();
                return '\"';
            }
            else if (c == '0')//'\0'
            {
                Source.MoveCursor();
                return '\0';
            }
            else
            {
                throw new InvalidEscapeSequenceException(Position, c.ToString());
            }
        }
        /// <summary>
        /// 从当前位置开始读取一个表示16进制的字符串
        /// </summary>
        /// <param name="maxlength">指定最长的字符个数,默认为4</param>
        /// <returns></returns>
        public string ReadHex(int maxlength = 4)
        {
            StringBuilder sb = new StringBuilder(maxlength);
            char c = Current;
            for (int i = 0; i < maxlength; i++)
            {
                if (c.IsHex())
                    sb.Append(c);
                else
                    break;
                c = Next();
            }
            return sb.ToString();
        }

        /// <summary> 
        /// 从当前位置开始读取一个数字
        /// </summary>
        /// <returns></returns>
        public string ReadNumeric()
        {
            char c = Current;
            if (!c.IsDigit() && c != '.')
                throw new TextLexerException(Position, 0, "无法读取数字，无效的字符：" + Current);

            StringBuilder sb = new StringBuilder(5);
            char p = Peek();
            if (c == '0' && (p == 'x' || p == 'X'))
            {//读取16进制常量
                if (!Next(2).IsHex())//之后非16进制字符
                    throw new TextLexerException(Position, 0, "16进制常量中存在词法异常");

                string value = ReadHex(int.MaxValue);
                return Convert.ToInt64(value, 16).ToString();//返回转换后的10进制数据。
            }
            else//读取十进制常量
            {
                bool hasdot = false;//记录是否已经出现过小数点
                bool hasE = false;//记录是否出现过科学计数法e
                while (!EOS())
                {
                    if (c == '.')
                    {//遇到小数点
                        if (hasdot)//第二次遇到小数点。完成。 
                            break;
                        else
                        {
                            hasdot = true;
                            if (hasE || !p.IsDigit())//第一次遇到小数点，但是已出现e或小数点后不是数字… 
                                break;
                        }
                    }
                    else if (c == 'e' || c == 'E')
                    {
                        if (hasE)//第二次遇到e，break
                            break;
                        else
                        {
                            hasE = true;
                            if (p == '+' || p == '-')//处理E后的符号
                            {
                                if (!Peek(2).IsDigit())//符号后不是数字，抛出异常
                                    throw new TextLexerException(Position, 0, "数字常量中存在词法异常");
                                else
                                {
                                    sb.Append(c).Append(p);
                                    Source.MoveCursor(2);//跳过e后的符号
                                    continue;
                                }
                            }
                        }
                    }
                    else if (!c.IsDigit())
                    {
                        break;
                    }
                    sb.Append(c);
                    c = Next();
                    p = Peek();
                }
            }

            return sb.ToString();
        }
        /// <summary>
        /// 从当前位置开始读取一个常规标识符（以下划线或字母开头，其后可以是下划线、字母、数字）
        /// </summary>
        /// <returns></returns>
        public string ReadIdentifier()
        {
            char c = Current;
            if (!c.IsLetter() && c != '_')
                return null; //throw new TextLexerException(Position, 0, "标识符必须以字母或下划线开头");
            StringBuilder sb = new StringBuilder(10);
            while (c.IsIdentifier())
            {
                sb.Append(c);
                c = Next();
            }
            return sb.ToString();
        }
        /// <summary>
        /// 读取一段静态文本（固定内容的字符串）
        /// </summary>
        /// <param name="staticcontent">要读取的静态字符串</param>
        /// <param name="ignoreCase">指示是否应该忽略字符的大小写进行比较</param>
        /// <returns></returns>
        public string ReadStatic(string staticcontent, bool ignoreCase = false)
        {
            if (ignoreCase)
            {
                return ReadStaticIgnoreCase(staticcontent);
            }
            if (string.IsNullOrEmpty(staticcontent))
            {
                return null;
            }
            for (int i = 0; i < staticcontent.Length; i++)
            {
                if (Peek(i) != staticcontent[i])
                {
                    //遇到不匹配的字符
                    return null;
                }
            }
            Source.MoveCursor(staticcontent.Length);
            return staticcontent;
        }


        /// <summary>
        /// 读取一段静态文本（固定内容的字符串），忽略文本大小写的差异
        /// </summary>
        /// <param name="staticcontent">要读取的静态字符串</param>
        /// <returns></returns>
        private string ReadStaticIgnoreCase(string staticcontent)
        {
            if (string.IsNullOrEmpty(staticcontent))
            {
                return null;
            }
            for (int i = 0; i < staticcontent.Length; i++)
            { //忽略大小写比较每个字符
                if (CharHelper.Compare(Peek(i), staticcontent[i], true) != 0)
                { //遇到不匹配的字符
                    return null;
                }
            }
            return Read(staticcontent.Length);
        }
        /// <summary>
        /// 从当前位置开始读取指定数量的字符
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        public string Read(int count)
        {
            char[] cs = new char[count];
            for (int i = 0; i < count; i++)
            {
                cs[i] = Current;
                Source.MoveCursor();
            }
            return new string(cs);
        }
        /// <summary>
        /// 从当前位置开始读取至文本源末尾的所有字符
        /// </summary>
        /// <returns></returns>
        public string ReadToEnd()
        {
            StringBuilder sb = new StringBuilder();
            while (!EOS())
            {
                sb.Append(Current);
                Source.MoveCursor();
            }
            return sb.ToString();
        }
        #endregion
    }

    /// <summary>
    /// 一般字符串解析程序
    /// </summary>
    public class OrdinaryTextLexer : _TextLexer
    {
        public OrdinaryTextLexer(string source)
            : base(source)
        {

        }

        public string ReadString()
        {
            return base.ReadString();
        }
    }
}
